#include <jni.h>

#include <string>
#include "Log.h"

extern "C" JNIEXPORT jstring JNICALL
Java_com_maniu_jnimaniu_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

//default  hiden  静态注册   动态
extern "C"
__attribute__ ((visibility ("default"))) void
Java_com_maniu_jnimaniu_MainActivity_func1(JNIEnv *env, jobject thiz) {
}
const char *APP_PACKAGE_NAME = "com.maniu.jnimaniu";
extern "C"
JNIEXPORT void JNICALL
Java_com_maniu_jnimaniu_MainActivity_func2(JNIEnv *env, jobject thiz, jstring data) {
//data  env 转换方法
    const char* data1=env->GetStringUTFChars(data, 0);

    env->ReleaseStringChars(data, reinterpret_cast<const jchar *>(data1));
}

// 验证是否通过
static jboolean auth = JNI_FALSE;
extern "C"
JNIEXPORT jstring JNICALL
Java_com_maniu_jnimaniu_MainActivity_func3(JNIEnv *env, jobject thiz) {
    std::string hello = "1232121321321秘钥";
    return env->NewStringUTF(hello.c_str());
}
//jobject
//jclass
extern "C"
JNIEXPORT void JNICALL
Java_com_maniu_jnimaniu_MainActivity_func4(JNIEnv *env, jobject thiz) {
//jobject--> class
    jclass activityClass=env->GetObjectClass(thiz);
//    方法签名 String 对象1    基本类型
    jfieldID idText = env->GetFieldID(activityClass, "text", "Ljava/lang/String;");
//    C字符串   1  java 字符串2

    jstring text1 = env->NewStringUTF("native 层修改");

    env->SetObjectField(thiz, idText, text1);
}


extern "C"
JNIEXPORT void JNICALL
Java_com_maniu_jnimaniu_MainActivity_func5(JNIEnv *env, jobject thiz) {

    jclass activityClass=env->GetObjectClass(thiz);
//  idText1  =    ArtMethod  sophix  andfix 修复原理
    jmethodID idText = env->GetMethodID(activityClass, "callBack", "(I)V");
//    方法字节码指令   快鱼  普通的java 反射
    jmethodID idText1 = env->GetMethodID(activityClass, "callBack", "(Ljava/lang/String;I)Ljava/lang/String;");
    jmethodID idText2 = env->GetMethodID(activityClass, "callBack", "([B)V");
    env->CallVoidMethod(thiz, idText, 2);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_maniu_jnimaniu_MainActivity_setAntiBiBCallback(JNIEnv *env, jobject thiz,
                                                        jobject callback) {




}
//动态  效率高
//    反编译  安全性高
void regist(JNIEnv *env, jobject thiz, jobject jCallback) {

    LOGD("--动态注册调用成功-->");
}
jint RegisterNatives(JNIEnv *env) {
    jclass activityClass = env->FindClass("com/maniu/jnimaniu/MainActivity");
    if (activityClass == NULL) {
        return JNI_ERR;
    }
    JNINativeMethod methods_MainActivity[] = {
            {
                "setAntiBiBCallback",
                        "(Lcom/maniu/jnimaniu/IAntiDebugCallback;)V",
                    (void *) regist
            }

    };

  return  env->RegisterNatives(activityClass,methods_MainActivity,
                         sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]));
}
//JNI_OnLoad   java
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
//    手机app   手机开机了
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }

    jint result = RegisterNatives(env);
//    函数注册

    return JNI_VERSION_1_6;
}

/**
 * public void getSignInfo() {
       try {
           PackageInfo packageInfo = getPackageManager().getPackageInfo(
                   getPackageName(), PackageManager.GET_SIGNATURES);
           Signature[] signs = packageInfo.signatures;
           Signature sign = signs[0];
           System.out.println(sign.toCharsString());
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
 */
/*
 * 获取全局 Application
 */
jobject getApplicationContext(JNIEnv *env) {
    jclass activityThread = env->FindClass("android/app/ActivityThread");
    jmethodID currentActivityThread = env->GetStaticMethodID(activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;");
    jobject at = env->CallStaticObjectMethod(activityThread, currentActivityThread);
    jmethodID getApplication = env->GetMethodID(activityThread, "getApplication", "()Landroid/app/Application;");
    return env->CallObjectMethod(at, getApplication);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_maniu_jnimaniu_MainActivity_init(JNIEnv *env, jclass clazz) {


    jclass binderClass = env->FindClass("android/os/Binder");
    jclass contextClass = env->FindClass("android/content/Context");
    jclass signatureClass = env->FindClass("android/content/pm/Signature");
    jclass packageNameClass = env->FindClass("android/content/pm/PackageManager");
    jclass packageInfoClass = env->FindClass("android/content/pm/PackageInfo");

    jmethodID packageManager = env->GetMethodID(contextClass, "getPackageManager", "()Landroid/content/pm/PackageManager;");
    jmethodID packageName = env->GetMethodID(contextClass, "getPackageName", "()Ljava/lang/String;");
    jmethodID toCharsString = env->GetMethodID(signatureClass, "toCharsString", "()Ljava/lang/String;");
    jmethodID packageInfo = env->GetMethodID(packageNameClass, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jmethodID nameForUid = env->GetMethodID(packageNameClass, "getNameForUid", "(I)Ljava/lang/String;");
    jmethodID callingUid = env->GetStaticMethodID(binderClass, "getCallingUid", "()I");

    jint uid = env->CallStaticIntMethod(binderClass, callingUid);

    // 获取全局 Application
    jobject context = getApplicationContext(env);

    jobject packageManagerObject = env->CallObjectMethod(context, packageManager);
    jstring packNameString = (jstring) env->CallObjectMethod(context, packageName);
    jobject packageInfoObject = env->CallObjectMethod(packageManagerObject, packageInfo, packNameString, 64);
    jfieldID signaturefieldID = env->GetFieldID(packageInfoClass, "signatures", "[Landroid/content/pm/Signature;");
    jobjectArray signatureArray = (jobjectArray) env->GetObjectField(packageInfoObject, signaturefieldID);
    jobject signatureObject = env->GetObjectArrayElement(signatureArray, 0);
    jstring runningPackageName = (jstring) env->CallObjectMethod(packageManagerObject, nameForUid, uid);

    if (runningPackageName) {// 正在运行应用的包名
        const char *charPackageName = env->GetStringUTFChars(runningPackageName, 0);
        if (strcmp(charPackageName, APP_PACKAGE_NAME) != 0) {
            return JNI_FALSE;
        }
        env->ReleaseStringUTFChars(runningPackageName, charPackageName);
    } else {
        return JNI_FALSE;
    }

    jstring signatureStr = (jstring) env->CallObjectMethod(signatureObject, toCharsString);
    const char *signature = env->GetStringUTFChars(
            (jstring) env->CallObjectMethod(signatureObject, toCharsString), NULL);

    env->DeleteLocalRef(binderClass);
    env->DeleteLocalRef(contextClass);
    env->DeleteLocalRef(signatureClass);
    env->DeleteLocalRef(packageNameClass);
    env->DeleteLocalRef(packageInfoClass);

    LOGE("current apk signature %s", signature);

    // 应用签名，通过 JNIDecryptKey.getSignature(getApplicationContext())
// 获取，注意开发版和发布版的区别，发布版需要使用正式签名打包后获取
    const char *SIGNATURE_KEY = "308201df30820148020101300d06092a864886f70d010105050030373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b30090603550406130255533020170d3232303232383131303835335a180f32303532303232313131303835335a30373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b300906035504061302555330819f300d06092a864886f70d010101050003818d0030818902818100a0b8d9f8eccd0f5df877f4f70bd362c173400f5ed1eb4cb1e28815a0116dd89b3c0daf6337439b18ce6d101ddf1ca564d30c91f51df75d1d5ab4751e2754a6e1ff59e64953eafb21190f1bcbc9433deb30e9d3c12b2f7b51ec09fbd4e21f374ad517d5d38ed5f056614085106f8c9f81993641056da5acbd0b9fca5fea5391ff0203010001300d06092a864886f70d0101050500038181001e841dd1a0e07363ddfd16b43c7dfd2657019c845a14617452549c2910b9739f1d210aec0d8dd0647c6401bfa00257a6342e2bf362073bbc37da8c2707456e3dfd38d21b170ba51c6170d79f39055100af388c7a02a228b35679f42c00cc0a4e12334cd1bb2788e23b5c83659af2f28e47051079430f12b40a4e4ab88eabd484";
    if (strcmp(signature, SIGNATURE_KEY) == 0) {
        LOGE("verification passed");
        env->ReleaseStringUTFChars(signatureStr, signature);
        auth = JNI_TRUE;
        return JNI_TRUE;
    } else {
        LOGE("verification failed");
        auth = JNI_FALSE;
        return JNI_FALSE;
    }
    return auth;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_maniu_jnimaniu_MainActivity_getKey(JNIEnv *env, jclass clazz) {
    const char *DECRYPT_KEY = "successful return 1232132131321!";
    if (auth) {
        return env->NewStringUTF(DECRYPT_KEY);
    } else {// 你没有权限，验证没有通过。
        return env->NewStringUTF("You don't have permission, the verification didn't pass.");
    }

}